﻿using mvcForum.Core.Interfaces.Data;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

namespace mvcForum.DataProvider.EntityFramework {

	public class Repository<TEntity> : RepositoryBase<TEntity> where TEntity : class {
		protected readonly DbContext context;
		protected readonly DbSet<TEntity> set;

		public Repository(DbContext context) {
			this.context = context;
			this.set = context.Set<TEntity>();
		}

		public override TEntity Create(TEntity newEntity) {
			this.set.Add(newEntity);
			return newEntity;
		}

		public override TEntity Read(String id) {
			return this.set.Find(id);
		}

		public override TEntity Read(Int32 id) {
			return this.set.Find(id);
		}

		public override TEntity Read(Guid id) {
			return this.set.Find(id);
		}

		public override TEntity Update(TEntity entity) {
			this.set.Attach(entity);
			this.context.Entry(entity).State = EntityState.Modified;
			return entity;
		}

		public override void Delete(TEntity entity) {
			this.set.Remove(entity);
		}

		public override void Delete(String id) {
			this.Delete(this.set.Find(id));
		}

		public override void Delete(Int32 id) {
			this.Delete(this.set.Find(id));
		}

		public override void Delete(Guid id) {
			this.Delete(this.set.Find(id));
		}

		/// <summary>
		/// Reads all elements.
		/// To avoid instantiating all elements, use LINQ to specify the needed elements
		/// and use the ToList() method to fetch those elements and only those from the database.
		/// </summary>
		/// <returns></returns>
		public override IEnumerable<TEntity> ReadAll() {
			return this.set;
		}

		/// <summary>
		/// Read one and only one element that matches the specification.
		/// </summary>
		/// <param name="spec"></param>
		/// <returns>One element or null</returns>
		public override TEntity ReadOne(ISpecification<TEntity> spec) {
			return this.set.Where(spec.IsSatisfied).FirstOrDefault();
		}

		public override TEntity ReadOne(Expression<Func<TEntity, Boolean>> criteria) {
			return this.set.Where(criteria).FirstOrDefault();
		}

		/// <summary>
		/// Read the elements that match the specification.
		/// </summary>
		/// <param name="spec">The specification for the elements wanted.</param>
		/// <returns></returns>
		public override IEnumerable<TEntity> ReadMany(ISpecification<TEntity> spec) {
			// Hopefully the spec is very specific and get the exact list of entities wanted.
			// We'll ToList it (get the entities from the database).
			return this.set.Where(spec.IsSatisfied).ToList();
		}

		public override IEnumerable<TEntity> ReadMany(Expression<Func<TEntity, Boolean>> criteria) {
			// Hopefully the spec is very specific and get the exact list of entities wanted.
			// We'll ToList it (get the entities from the database).
			return this.set.Where(criteria).ToList();
		}
	}
}